package network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.TargetDataLine;

import model.AudioFormatWrapper;
import model.Call;
import model.EnumCallStatus;
import model.Model;

public class UDPRecordAndDispatch extends Thread {

	
	private TargetDataLine lineMic;
	private boolean running = true;
	private int bufferLength=640;
	private byte[] buffer;
	private Model model;
	final static float MAX_8_BITS_SIGNED = Byte.MAX_VALUE;
	final static float MAX_8_BITS_UNSIGNED = 0xff;
	final static float MAX_16_BITS_SIGNED = Short.MAX_VALUE;
	final static float MAX_16_BITS_UNSIGNED = 0xffff;
	private AudioFormat format;
	private float level;

	public UDPRecordAndDispatch(Model model) {
		this.model=model;
		AudioFormatWrapper audioFormatWrapper = new AudioFormatWrapper();
		format = audioFormatWrapper.getAudioFormat();
		DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
		try {
			lineMic = (TargetDataLine) AudioSystem.getLine(info);
			lineMic.open(format);
		} catch (LineUnavailableException e) {
			e.printStackTrace();
		}
		lineMic.start();
		buffer = new byte[bufferLength];

		
	}
	
	public void run(){
		
		System.out.println("run du dispatcher");
		
		while(running){
			
			lineMic.read(buffer, 0, buffer.length);
			calculateLevel(buffer, 0, 0);
			for(Call call : model.getAllCall()){
				if(call.getUdpRecordAndSend() != null){
					
					//System.out.println(level+"/"+call.getVoiceActivationLevel()/100);
					if(call.getMuteMicrophone() == false  && level > call.getVoiceActivationLevel()/100 &&  call.getStatus() == EnumCallStatus.ACTIV_ESTABLISHED){
						DatagramPacket outPacket;
						DatagramSocket datagramSocket;
						try {
							datagramSocket = new DatagramSocket();
							outPacket = new DatagramPacket(buffer, buffer.length, call.getUdpRecordAndSend().getAddressToSend(), call.getUdpRecordAndSend().getPortToSend());
							datagramSocket.send(outPacket);	
						} catch (SocketException e) {
							
							e.printStackTrace();
						} catch (IOException e) {
							
							e.printStackTrace();
						}
						
				}
			}
			
		}
	}
	}
	
	
	private void calculateLevel (byte[] buffer,
			int readPoint,
			int leftOver) {
		int max = 0;
		boolean use16Bit = (format.getSampleSizeInBits() == 16);
		boolean signed = (format.getEncoding() ==
				AudioFormat.Encoding.PCM_SIGNED);
		boolean bigEndian = (format.isBigEndian());
		if (use16Bit) {
			for (int i=readPoint; i<buffer.length-leftOver; i+=2) {
				int value = 0;
				// deal with endianness
				int hiByte = (bigEndian ? buffer[i] : buffer[i+1]);
				int loByte = (bigEndian ? buffer[i+1] : buffer [i]);
				if (signed) {
					short shortVal = (short) hiByte;
					shortVal = (short) ((shortVal << 8) | (byte) loByte);
					value = shortVal;
				} else {
					value = (hiByte << 8) | loByte;
				}
				max = Math.max(max, value);
			} // for
		} else {
			// 8 bit - no endianness issues, just sign
			for (int i=readPoint; i<buffer.length-leftOver; i++) {
				int value = 0;
				if (signed) {
					value = buffer [i];
				} else {
					short shortVal = 0;
					shortVal = (short) (shortVal | buffer [i]);
					value = shortVal;
				}
				max = Math.max (max, value);
			} // for
		} // 8 bit
		// express max as float of 0.0 to 1.0 of max value
		// of 8 or 16 bits (signed or unsigned)
		if (signed) {
			if (use16Bit) { level = (float) max / MAX_16_BITS_SIGNED; }
			else { level = (float) max / MAX_8_BITS_SIGNED; }
		} else {
			if (use16Bit) { level = (float) max / MAX_16_BITS_UNSIGNED; }
			else { level = (float) max / MAX_8_BITS_UNSIGNED; }
		}
	} // calculateLevel
}
	


